package it.uniroma2.exp.dtk;

import it.uniroma2.dtk.dt.GenericDT;
import it.uniroma2.exp.AbstractExperiment;
import it.uniroma2.exp.tools.AvgVarCalculator;
import it.uniroma2.tk.TreeKernel;
import it.uniroma2.util.math.ArrayMath;
import it.uniroma2.util.math.statistical.SpearmanCorrelation;
import it.uniroma2.util.tree.RandomTreeGenerator;
import it.uniroma2.util.tree.Tree;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Vector;

/**
 * @author Lorenzo Dell'Arciprete
 * 
 * This experiment evaluates how similar are two kernel results for trees taken from a certain corpus.
 * The available corpora are artificially generated trees, question classification trees and
 * textual entrailment recognition trees [not available yet].
 * Other than the Tree Kernel and Distributed Tree Kernel, a Zero Tree Kernel is included, 
 * that is simply a Tree Kernel with lambda = 0.
 * The kernel value similarity measures are Spearman Correlation and Ratio 
 */
public class KernelComparison extends AbstractExperiment {
	
	public enum OPS { TK, DTK, ZTK };
	public enum TYPES { artificial, rte, qc };
	//The location of the Question Classification trees file (in SVM Light format)
	public final String inputTreesQC = "/home/lorenzo/esperimenti/dtk/qc/correlationTestData/corr_test_data.dat";

	public GenericDT dtk = null;
	
	//The two kernels to be compared
	public static final OPS OP1 = OPS.DTK;
	public static final OPS OP2 = OPS.TK;
	//The corpora for the trees
	public static final TYPES TYPE = TYPES.qc;
	//The experiment may be repeated with different random values
	public static final int TRIALS = 1;
	
	public static void main(String [] argv) throws Exception {
		KernelComparison kc = new KernelComparison();
		kc.setRandomTreeNodes(30);
		kc.setUsePos(TYPE != TYPES.artificial);
		kc.runAll();
	}

	@Override
	protected void runExperiment() throws Exception {
		TreeKernel.lexicalized = lexicalized;
		TreeKernel.lambda = lambda;
		dtk.setLambda(lambda);
		
		AvgVarCalculator avgSpearCor = new AvgVarCalculator();
		AvgVarCalculator avgRatioCor = new AvgVarCalculator();
		for (int trial=0; trial<TRIALS; trial++) {
			dtk = new GenericDT(trial+randomOffset, vectorSize, usePos, lexicalized, compositionType);
			out.println("****** trial "+(trial+1)+" of "+TRIALS+" *******");
			switch(TYPE) {
			case artificial: computeArtificial(avgSpearCor, avgRatioCor); break;
			case rte: computeRTE(avgSpearCor, avgRatioCor); break;
			case qc: computeQC(avgSpearCor, avgRatioCor); break;
			}
			out.print(String.format("spearman %.3f\t", avgSpearCor.getLastSample()));
			out.println(String.format("ratio %.3f\t", avgRatioCor.getAvg()));
		}

		out.println("\n******* FINAL RESULTS ******");
		out.println(OP1.toString()+" vs "+OP2.toString());
		out.println("Corpus: "+TYPE.toString());
		out.println("Trials: "+TRIALS);
		out.println("Spearman\tRatio");
		out.print(String.format("%.3f (%.3f)\t", avgSpearCor.getAvg(), avgSpearCor.getVar()));
		out.println(String.format("%.3f (%.3f)\t", avgRatioCor.getAvg(), avgRatioCor.getVar()));
	}
	
	public void computeRTE(AvgVarCalculator spear, AvgVarCalculator ratio) throws Exception {
		out.println("Not available yet!");
		//TODO to be integrated with ARTE classes
	}
	
	public void computeArtificial(AvgVarCalculator spear, AvgVarCalculator ratio) throws Exception {
		RandomTreeGenerator rtg = new RandomTreeGenerator(randomOffset);
		Vector<Double> a_vec = new Vector<Double>(); 
		Vector<Double> b_vec = new Vector<Double>();
		SpearmanCorrelation corr = new SpearmanCorrelation();
		for (int i=0; i<1000; i++) {
			out.print('.');
			if (i%100 == 0) out.println("" +i);
			String treeh = rtg.generateRandomTree(randomTreeLabels, randomTreeMaxDegree, randomTreeNodes);
			Tree xh = Tree.fromPennTree(treeh);
			String treet = rtg.generateRandomTree(randomTreeLabels, randomTreeMaxDegree, randomTreeNodes);
			Tree xt = Tree.fromPennTree(treet);
			try {
				double value1 = op(xt, xh, OP1);
				double value2 = op(xt, xh, OP2);
				a_vec.add(value1);
				b_vec.add(value2);
				if (value2 != 0)
					ratio.addSample(value1/value2);
			} catch (Exception e) {
				out.println(e.getMessage());
			}
		}
		out.println("end");
		float[] a = new float[a_vec.size()];
		for (int i=0; i< a_vec.size();i++) a[i]= a_vec.elementAt(i).floatValue();
		float[] b = new float[b_vec.size()];
		for (int i=0; i< b_vec.size();i++) b[i]= b_vec.elementAt(i).floatValue();
		spear.addSample(corr.spearmanCorrelationCoefficient(a, b));
	}
	
	public void computeQC(AvgVarCalculator spear, AvgVarCalculator ratio) throws Exception {
		Vector<Double> a_vec = new Vector<Double>(); 
		Vector<Double> b_vec = new Vector<Double>();
		SpearmanCorrelation corr = new SpearmanCorrelation();
		BufferedReader in = new BufferedReader(new FileReader(inputTreesQC));
		int count = 0;
		while (true) {
			String treet = in.readLine();
			String treeh = in.readLine();
			if (treet == null || treeh == null)
				break;
			treet = treet.substring(treet.indexOf("|BT|")+4, treet.indexOf("|ET|")).trim(); 
			Tree xt = Tree.fromPennTree(treet);
			treeh = treeh.substring(treeh.indexOf("|BT|")+4, treeh.indexOf("|ET|")).trim(); 
			Tree xh = Tree.fromPennTree(treeh);
			double value1 = op(xt, xh, OP1);
			double value2 = op(xt, xh, OP2);
			a_vec.add(value1);
			b_vec.add(value2);
			if (value2 != 0)
				ratio.addSample(value1/value2);
			out.print('.');
			if (count%100 == 0) out.println("" +count);
			count += 2;
		}
		in.close();
		float[] a = new float[a_vec.size()];
		for (int i=0; i< a_vec.size();i++) a[i]= a_vec.elementAt(i).floatValue();
		float[] b = new float[b_vec.size()];
		for (int i=0; i< b_vec.size();i++) b[i]= b_vec.elementAt(i).floatValue();
		spear.addSample(corr.spearmanCorrelationCoefficient(a, b));
	}
	
	public double op(Tree xt, Tree xh, OPS op) throws Exception {
		switch(op) {
			case TK: return TK(xt, xh); 
			case DTK: return DTK(xt, xh); 
			case ZTK: return ZTK(xt, xh); 
			default: throw new Exception("Unknown operation!");
		}
	}
	
	public double TK(Tree xt, Tree xh) {
		return TreeKernel.value(xt, xh);
	}
	
	public double DTK(Tree xt, Tree xh) throws Exception {
		double[] vectort = dtk.dt(xt);
		double[] vectorh = dtk.dt(xh);
		return ArrayMath.dot(vectort,vectorh);
	}
	
	public double ZTK(Tree xt, Tree xh) {
		double oldLambda = TreeKernel.lambda;
		TreeKernel.lambda = 0;
		double result = TK(xt, xh);
		TreeKernel.lambda = oldLambda;
		return result;
	}
}
